/*

* Copyright (c) 2007, National University of Singapore (NUS)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without 
* modification, are permitted provided that the following conditions 
* are met:
*
*   * Redistributions of source code must retain the above copyright notice, 
*     this list of conditions,the authors and the following disclaimer.
*   * Redistributions in binary form must reproduce the above copyright notice,
*     this list of conditions,the authors and the following disclaimer in
*     the documentation and/or other materials provided with the distribution.
*   * Neither the name of the university nor the names of its 
*     contributors may be used to endorse or promote products derived from 
*     this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
* POSSIBILITY OF SUCH DAMAGE.
* 
* Author: Wenwei Xue (dcsxw@nus.edu.sg)
*
*/
package psg.queryprocessor;
import psg.servicemanager.*;

/**
 * This class implements an evaluation plan for a context query at a PSG. 
 * We assume a query only involves a single physical space 
 * in the current prototype.
 */
public class QueryPlan {
	
	String queryString;  // query string that this plan is parsed from
	
	public static final byte DATA_ACQUISITION = 0x01;	// one-time, pull-based data acquisition
	public static final byte EVENT_SUBSCRIPTION = 0x02;	// continuous, push-based event detection and notification
	byte type;
	
	public String attrs []; // list of context attributes in the SELECT or SUBSCRIBE clause
	
	public String domain;	// context domain of the physical space; NULL if no FROM clause in query specification
	
	QueryCondition cons []; // predicates in the WHERE clause; NULL if no WHERE clause in query specification
	
	public String errStr = null;	// a string indicates whether this query plan is generated from parsing a valid query string
							// if not, the error message is contained in this string
	
	ContextDataServices cds = null;
		
	/**
	 * This function obtains the query result from the PSG.
	 * For a data acquisition query, the result is the current values of a list of context attribute.
	 * For an event subscription query, the result is the Socket Address of the Event Server Socket.
	 * @return string representation of the query result
	 *         for a data acquisition query: (attr1, val1); (attr2, val2); ... (attrN, valN)
	 *         for an event subscription query: gatewayIP:port; (event1, connectionID1); ... (eventN, connectionIDN)
	 */
	public String getQueryResult() {
		
		String result = "", socketAddress [] = null;
		int i;
		if (type == QueryPlan.DATA_ACQUISITION)
		{
			for (i=0;i<attrs.length;i++)
				result += "(" + attrs[i] + ", " + cds.getContext(domain, attrs[i]) + "); ";
				
			result = result.substring(0,result.length()-2);			
			
		}
		else if (type == QueryPlan.EVENT_SUBSCRIPTION) 
		{
			for (i=0;i<attrs.length;i++)
			{
				String name = attrs[i];
				if (name.indexOf(ContextAttribute.eventNames[2].toLowerCase())== 0)
				{
					int start, end;
			
					start = ContextAttribute.eventNames[2].length();
			
					start = name.indexOf("(",start) + 1;
			
					end = name.indexOf(")",start);
				
					name = name.substring(start,end).trim();
					
					socketAddress = cds.getContextChange(domain, name);				
				}
				else
					socketAddress = cds.subscribeEvent(domain,attrs[i]);
				
				if (i==0)
					result += socketAddress[0] + ":" + socketAddress[1] + ";";
				
				result += "(" + attrs[i] + ", " + socketAddress[2] + "); ";				
			}					
						
			result = result.substring(0,result.length()-2);
			
		}	
		
		return result;
	
	}  // getQueryResult
	
	/**
	 * This function evaluates whether the condition of this query is satisfied at this time.
	 * @return TURE if the condition is satisfied; FALSE otherwise
	 */
	public boolean evaluateQueryCondition() {
		
		if (cons == null) return true;
		
		int i;
		for (i=0;i<cons.length;i++)
		{
			if (!cds.filterContext(domain,cons[i].attr,cons[i].op,cons[i].constant))
				return false;
		}
		
		return true;
		
	}  // evaluateQueryCondition
	
	/**
	 * This function prints out information encapsulated in a query plan.
	 */
	public void print() {	
		
		if (errStr != null)
		{
			System.out.println("Error message: " + errStr);
			
			return;
			
		}
		
		System.out.print("Type of query: ");
		if (type==QueryPlan.DATA_ACQUISITION)
			System.out.println("data acquisition");
		else
			System.out.println("event subscription");
			
		if (domain != null)
			System.out.println("Context domain: " + domain + " ");
		else
			System.out.println("No context domain specified.");
		
		System.out.print("List of context attributes: ");	
		int i;
		for (i=0;i<attrs.length;i++)
			System.out.print(attrs[i] + " ");
		System.out.println();
	
		if (cons == null)
			System.out.println("No query predicate.");
		else
	 {
	 		System.out.println("List of query predicates: ");
	 		for (i=0;i<cons.length;i++)
				System.out.println("(" + (i+1) + ") " + cons[i].attr + " " + cons[i].op + " " + cons[i].constant);
	 }		
		
		System.out.println("Query condition: " + evaluateQueryCondition());
		
		System.out.println("Query result: " + getQueryResult());
		
	}	// print

} // class